/**
 * session管理模块
 * 设计session类保存用户信息以及一个唯一的sessionID
 * 设计session管理类负责管理session对象
 */
#pragma once

#include"Detail.hpp"
#include<unordered_map>

#define TIME_FOREVER -1/*session永久有效标识*/
#define TIME_OUT 30000/*定时任务的定时时间*/

namespace OnlineGoBang
{
    typedef enum{UNLOGIN,LOGIN}user_statu;//用户状态

    //session模块
    class Session
    {
    public:
        Session(const uint64_t& sid)
            :_sid(sid)
        {
            LOG(INFO,"%lu Session信息创建\n",_sid);
        }
        ~Session()
        {
            LOG(INFO,"%lu Session信息销毁\n",_sid);
        }
        //设置用户ID
        void SetUser(const uint64_t& uid)
        {
            _uid=uid;
        }
        //获取用户ID
        uint64_t GetUser()
        {
            return _uid;
        }
        //获取sid
        uint64_t GetSid()
        {
            return _sid;
        }
        //用户是否是登录状态
        bool isLogin()
        {
            return _statu==LOGIN;
        }
        //设置用户状态
        void SetStatu(const user_statu& statu)
        {
            _statu=statu;
        }
        //设置定时器
        void SetTimer(const wsserver_t::timer_ptr& timer)
        {
            _timer=timer;
        }
        //获取定时器
        wsserver_t::timer_ptr& GetTimer()
        {
            return _timer;
        }

    private:
        uint64_t _sid;//sessionID
        uint64_t _uid;//用户ID
        user_statu _statu;//用户状态
        wsserver_t::timer_ptr _timer;//定时器
    };

    using SessionPtr=std::shared_ptr<Session>;

    //session管理模块
    class SessionManager
    {
    public:
        SessionManager(wsserver_t* server)
            :_next_sid(1)
            ,_server(server)
        {}
        SessionManager(std::shared_ptr<wsserver_t>& server)
            :_next_sid(1)
            ,_server(server)
        {}
        ~SessionManager()
        {}
        //创建session对象
        SessionPtr CreateSession(const uint64_t& uid,const user_statu& statu)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            SessionPtr sp(new Session(_next_sid));
            sp->SetStatu(statu);
            sp->SetUser(uid);
            _sessions.insert({_next_sid,sp});
            _next_sid++;
            return sp;
        }
        //获取session对象
        SessionPtr GetSession(const uint64_t& sid)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _sessions.find(sid);
            if(it==_sessions.end())
            {
                return SessionPtr();
            }
            return it->second;
        }
        //添加已存在的session对象
        void AppendSession(const SessionPtr& session)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _sessions.insert({session->GetSid(),session});
        }
        //设置session过期时间
        void SetSessionExpireTime(const uint64_t& sid,int ms)
        {
            //借助websocket的定时器完成对session对象生命周期的管理
            SessionPtr sp=GetSession(sid);
            if(sp.get()==nullptr)
            {
                return;
            }
            wsserver_t::timer_ptr tp=sp->GetTimer();
            if(tp.get()==nullptr)//当前session为永久存在的(没有设置定时器)
            {
                if(ms==TIME_FOREVER)//设置session为永久存在
                {
                    // LOG(DEBUG,"设置session为永久有效\n");
                    return;
                }
                else//设置session信息ms时间后删除
                {
                    //设置websocket定时器定时任务
                    //LOG(DEBUG,"设置定时删除任务\n");
                    wsserver_t::timer_ptr timer=_server->set_timer(ms,std::bind(&SessionManager::RemoveSession,this,sid));
                    //设置当前session的定时器
                    sp->SetTimer(timer);
                }
            }
            else//当前session设置了定时删除任务
            {   
                if(ms==TIME_FOREVER)//设置session为永久存在的
                {
                    // LOG(DEBUG,"设置session为永久有效\n");
                    //取消当前session已经设置的定时任务
                    //会导致任务立即执行(服务器感知到时),会删除_sessions中当前session信息
                    tp->cancel();//可能不会立即执行
                    //重新添加session信息
                    sp->SetTimer(wsserver_t::timer_ptr());//设置定时器为空
                    //设置定时任务把当前session信息添加进_sessions中管理
                    _server->set_timer(0,std::bind(&SessionManager::AppendSession,this,sp));
                }
                else//重置定时任务
                {
                    tp->cancel();
                    //重新添加session信息
                    sp->SetTimer(wsserver_t::timer_ptr());//设置定时器为空
                    _server->set_timer(0,std::bind(&SessionManager::AppendSession,this,sp));
                    //重新设置定时删除任务
                    //LOG(DEBUG,"设置定时删除任务\n");
                    wsserver_t::timer_ptr timer=_server->set_timer(ms,std::bind(&SessionManager::RemoveSession,this,sid));
                    sp->SetTimer(timer);
                }   

            }
        }
        //销毁session对象
        void RemoveSession(const uint64_t& sid)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _sessions.erase(sid);
        }

    private:
        uint64_t _next_sid;//sid
        std::mutex _mutex;
        std::unordered_map<uint64_t,SessionPtr> _sessions;//sid和session对象之间的映射
        std::shared_ptr<wsserver_t> _server;//设置定时任务的服务器
    };
}